package termbox

import (
	"bytes"
	"encoding/gob"
	"os"
	"os/exec"
	"parse/src/events/termbox-go/go-runewidth"
	"unicode/utf8"
)

var arrowLeft = '←'
var arrowRight = '→'

func init() {
	if runewidth.EastAsianWidth {
		arrowLeft = '<'
		arrowRight = '>'
	}
}

// Draws the EditBox in the given location, 'h' is not used at the moment
func (eb *EditBox) Draw(x, y, w, h int) {
	eb.AdjustVOffset(w)

	const coldef = ColorDefault
	const colred = ColorRed

	fill(x, y, w, h, Cell{Ch: ' '})

	t := eb.Text
	lx := 0
	tabstop := 0
	for {
		rx := lx - eb.LineVoffset
		if len(t) == 0 {
			break
		}

		if lx == tabstop {
			tabstop += tabstop_length
		}

		if rx >= w {
			SetCell(x+w-1, y, arrowRight,
				colred, coldef)
			break
		}

		r, size := utf8.DecodeRune(t)
		if r == '\t' {
			for ; lx < tabstop; lx++ {
				rx = lx - eb.LineVoffset
				if rx >= w {
					goto next
				}

				if rx >= 0 {
					SetCell(x+rx, y, ' ', coldef, coldef)
				}
			}
		} else {
			if rx >= 0 {
				SetCell(x+rx, y, r, coldef, coldef)
			}
			lx += runewidth.RuneWidth(r)
		}
	next:
		t = t[size:]
	}

	if eb.LineVoffset != 0 {
		SetCell(x, y, arrowLeft, colred, coldef)
	}
}

// 深度复制
func DeepCopy(dst, src interface{}) error {
	var buf bytes.Buffer
	if err := gob.NewEncoder(&buf).Encode(src); err != nil {
		return err
	}
	return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}

// 清除窗口内容
func cleanCmd() {
	cmd := exec.Command("cmd", "/c", "cls")
	cmd.Stdout = os.Stdout
	cmd.Run()
}

// 向上走
func MoveUp(i int) {
	w := back_buffer.width
	ll := len(back_buffer.cells)
	var result = make([]Cell, ll)
	lastY := 0
	for k, value := range back_buffer.cells {
		x := k % w
		y := k/w - 1
		if lastY != y {
			lastY = y
		}
		if y >= 0 {
			result[y*w+x] = value
		}
	}
	_ = Clear(ColorDefault, ColorDefault)
	Flush()
	for k, value := range result {
		x := k % w
		y := k / w
		back_buffer.cells[y*w+x] = value
	}
	a, b := Size()
	var t = make([]rune, a)
	var c = make([][]rune, b)
	for key, _ := range c {
		c[key] = t
	}
	for k, diff := range result {
		x := k % w
		y := k / w
		c[y][x] = diff.Ch
	}
	for y, yV := range c {
		res := ""
		for _, value := range yV {
			res += string(value)
		}
		if res != "" {
			Tbprint(-1, y, ColorDefault, ColorDefault, res)
		}
	}
}

// Adjusts line visual offset to a proper value depending on width
func (eb *EditBox) AdjustVOffset(width int) {
	ht := preferred_horizontal_threshold
	max_h_threshold := (width - 1) / 2
	if ht > max_h_threshold {
		ht = max_h_threshold
	}

	threshold := width - 1
	if eb.LineVoffset != 0 {
		threshold = width - ht
	}
	if eb.CursorVoffset-eb.LineVoffset >= threshold {
		eb.LineVoffset = eb.CursorVoffset + (ht - width + 1)
	}

	if eb.LineVoffset != 0 && eb.CursorVoffset-eb.LineVoffset < ht {
		eb.LineVoffset = eb.CursorVoffset - ht
		if eb.LineVoffset < 0 {
			eb.LineVoffset = 0
		}
	}
}

func (eb *EditBox) MoveCursorTo(boffset int) {
	eb.CursorBoffset = boffset
	eb.CursorVoffset, eb.CursorCoffset = voffset_coffset(eb.Text, boffset)
}

func (eb *EditBox) RuneUnderCursor() (rune, int) {
	return utf8.DecodeRune(eb.Text[eb.CursorBoffset:])
}

func (eb *EditBox) RuneBeforeCursor() (rune, int) {
	return utf8.DecodeLastRune(eb.Text[:eb.CursorBoffset])
}

func (eb *EditBox) MoveCursorOneRuneBackward() {
	if eb.CursorBoffset == 0 {
		return
	}
	_, size := eb.RuneBeforeCursor()
	eb.MoveCursorTo(eb.CursorBoffset - size)
}

func (eb *EditBox) MoveCursorOneRuneForward() {
	if eb.CursorBoffset == len(eb.Text) {
		return
	}
	_, size := eb.RuneUnderCursor()
	eb.MoveCursorTo(eb.CursorBoffset + size)
}

func (eb *EditBox) MoveCursorToBeginningOfTheLine() {
	eb.MoveCursorTo(0)
}

func (eb *EditBox) MoveCursorToEndOfTheLine() {
	eb.MoveCursorTo(len(eb.Text))
}

func (eb *EditBox) DeleteRuneBackward() {
	if eb.CursorBoffset == 0 {
		return
	}

	eb.MoveCursorOneRuneBackward()
	_, size := eb.RuneUnderCursor()
	eb.Text = byte_slice_remove(eb.Text, eb.CursorBoffset, eb.CursorBoffset+size)
}

func (eb *EditBox) DeleteRuneForward() {
	if eb.CursorBoffset == len(eb.Text) {
		return
	}
	_, size := eb.RuneUnderCursor()
	eb.Text = byte_slice_remove(eb.Text, eb.CursorBoffset, eb.CursorBoffset+size)
}

func (eb *EditBox) DeleteTheRestOfTheLine() {
	eb.Text = eb.Text[:eb.CursorBoffset]
}

func (eb *EditBox) InsertRune(r rune) {
	var buf [utf8.UTFMax]byte
	n := utf8.EncodeRune(buf[:], r)
	eb.Text = byte_slice_insert(eb.Text, eb.CursorBoffset, buf[:n])
	eb.MoveCursorOneRuneForward()
}

// Please, keep in mind that cursor depends on the value of LineVoffset, which
// is being set on Draw() call, so.. call this method after Draw() one.
func (eb *EditBox) CursorX() int {
	return eb.CursorVoffset - eb.LineVoffset
}

// 字符串相乘
func Multi(str string, num int) string {
	res := ""
	for i := 0; i < num; i++ {
		res += str
	}
	return res
}
func Tbprint(x, y int, fg, bg Attribute, msg string) {
	a, _ := Size()
	if len(msg) < a-10 {
		msg += Multi(" ", a-10-len(msg))
	}
	for _, c := range msg {
		SetCell(x, y, c, fg, bg)
		x += runewidth.RuneWidth(c)
	}
}

func fill(x, y, w, h int, cell Cell) {
	for ly := 0; ly < h; ly++ {
		for lx := 0; lx < w; lx++ {
			SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg)
		}
	}
}

func rune_advance_len(r rune, pos int) int {
	if r == '\t' {
		return tabstop_length - pos%tabstop_length
	}
	return runewidth.RuneWidth(r)
}

func voffset_coffset(text []byte, boffset int) (voffset, coffset int) {
	text = text[:boffset]
	for len(text) > 0 {
		r, size := utf8.DecodeRune(text)
		text = text[size:]
		coffset += 1
		voffset += rune_advance_len(r, voffset)
	}
	return
}

func byte_slice_grow(s []byte, desired_cap int) []byte {
	if cap(s) < desired_cap {
		ns := make([]byte, len(s), desired_cap)
		copy(ns, s)
		return ns
	}
	return s
}

func byte_slice_remove(text []byte, from, to int) []byte {
	size := to - from
	copy(text[from:], text[to:])
	text = text[:len(text)-size]
	return text
}

func byte_slice_insert(text []byte, offset int, what []byte) []byte {
	n := len(text) + len(what)
	text = byte_slice_grow(text, n)
	text = text[:n]
	copy(text[offset+len(what):], text[offset:])
	copy(text[offset:], what)
	return text
}

const preferred_horizontal_threshold = 5
const tabstop_length = 8

type EditBox struct {
	Text             []byte
	LineVoffset      int
	CursorLineOffset int // 当前行数
	CursorBoffset    int // 在byte中的游标位置偏移量
	CursorVoffset    int // 在屏幕中的游标的位置
	CursorCoffset    int // 在unicode中的位置
}
